#ifndef HEADER_DEF_H
#define HEADER_DEF_H

#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>

namespace usnet {
enum RWFlag : int32_t {
    RW_DEFAULT = 0,
    RW_SYNC = 1,
};
// payload protocol type
enum class PPT : uint16_t {
    NONE = 0x0000,
    TCP  = 0x0006,
    UDP  = 0x0011,
    IPv4 = 0x0800,
    IPv6 = 0x86dd,
    ARP  = 0x0806,
};

enum TCPFlag : uint8_t {
    FIN_BIT = 0x01,
    SYN_BIT = 0x02,
    RST_BIT = 0x04,
    PSH_BIT = 0x08,
    ACK_BIT = 0x10,
    URG_BIT = 0x20,
    ECE_BIT = 0x40,
    RWR_BIT = 0x80,
};

enum class TCPStatus {
    CLOSED,
    SYN_SENT,
    SYN_RECV,
    ESTABLISHED,
    LISTENING,
    FIN_WAIT1,
    CLOSE_WAIT,
    FIN_WAIT2,
    LAST_ACK,
    TIME_WAIT,
};

struct TCPHeader {
    void Dump(FILE* fp);

    uint16_t be_lport_;     // source port in big endian byte order
    uint16_t be_rport_;     // dest port in big endian byte order
    uint32_t be_syn_seq_;   // byte sequence number in byte order
    uint32_t be_ack_seq_;   // ack sequence number byte order
    uint8_t  hdr_len_;      // header size
    uint8_t  tcp_flags_;    // tcp flags
    uint16_t be_rwnd_size_; // receive window size
    uint16_t be_checksum_;  // header checksum
    uint16_t be_urg_ptr_;   // urgent pointer
} __attribute__((packed));

struct UDPHeader {
    void Dump(FILE* fp);

} __attribute__((packed));

struct IP4Header {
    void Dump(FILE* fp);
    uint8_t  ipvhl_;        // IP version(4 bits) + header length(4bits)
    uint8_t  tos_;          // type of service
    uint16_t be_len_;       // total length(including header) of IP Packet 
    uint16_t be_pid_;       // IP packet id
    uint16_t be_off_;       // flag(3 bits) + slice offset(13 bit)
    uint8_t  ttl_;          // time to live
    uint8_t  protocol_;     // protocol
    uint16_t be_checksum_;  // header checksum
    uint32_t be_laddr_;     // source IPv4 address
    uint32_t be_raddr_;     // dest IPv4 address
} __attribute__((packed));

struct IP6Header {
    void Dump(FILE* fp);

    uint32_t be_vcfl_;      // IP version(4 bits) + traffic class(8 bits) + flow label(20 bits)
    uint16_t be_size_;      // payload size
    uint8_t  next_hdr_;     // next header
    uint8_t  hop_lim_;      // hop limit
    uint8_t  laddr_[16];    // source IPv6 address
    uint8_t  raddr_[16];    // dest IPv6 address
} __attribute__((packed));

struct ARPHeader {
    void Dump(FILE* fp);
    
} __attribute__((packed));

struct EthHeader {
    void Dump(FILE* fp);
    
    uint8_t  dst_ethaddr_[6];
    uint8_t  src_ethaddr_[6];
    uint16_t be_protocol_;
} __attribute__((packed));

struct IP4PseudoHdr {
    IP4PseudoHdr(
        const uint32_t laddr,
        const uint32_t raddr,
        const uint8_t  protocol,
        const uint16_t paylen):
        be_laddr_   { laddr },
        be_raddr_   { raddr },
        protocol_   { protocol },
        be_paylen_  { paylen }
    {
        // nothing to do
    }
    void Dump(FILE* fp);
    
    uint32_t  be_laddr_     { };
    uint32_t  be_raddr_     { };
    uint8_t   zeros_        { };
    uint8_t   protocol_     { };
    uint16_t  be_paylen_    { };  /* udp hdr + payload */
} __attribute__((packed));

struct IP6PseudoHdr {
    IP6PseudoHdr(
        const uint8_t* laddr,
        const uint8_t* raddr,
        const uint32_t paylen,
        const uint8_t  next_hdr):
        be_paylen_ { paylen },
        next_hdr_  { next_hdr }
    {
        memcpy(laddr_, laddr, sizeof(laddr_));
        memcpy(raddr_, raddr, sizeof(raddr_));
    }
    void Dump(FILE* fp);
    
    uint8_t  laddr_[16]     { };
    uint8_t  raddr_[16]     { };
    uint32_t be_paylen_     { };
    uint8_t  zeros_[3]      { };
    uint8_t  next_hdr_      { };
} __attribute__((packed));

uint64_t InetCheckSum(const uint16_t* data, const uint32_t len);

} // end of namespace usnet
#endif